
package com.hlkj.pay.app.order.impl;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.pojo.LocalContext;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.pay.app.merchant.impl.CommonChannelCallHandler;
import com.hlkj.pay.app.order.ISettleOrderAppService;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.common.OpenApiResultCode;
import com.hlkj.pay.common.PayResultCode;
import com.hlkj.pay.dto.LocalMerchantRequest;
import com.hlkj.pay.dto.merchant.access.MerchantAccessBalanceDto;
import com.hlkj.pay.dto.order.notify.OrderSettleNotifyDto;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.gateway.merchant.IMerchantAccessSettleGateway;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAccessInfoDO;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.OrderSettleDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.service.merchant.IMerchantAccessService;
import com.hlkj.pay.service.merchant.IMerchantApplicationService;
import com.hlkj.pay.service.order.ISettleOrderService;
import com.hlkj.pay.service.pay.IProviderService;
import com.hlkj.pay.util.MerchantUtils;
import com.hlkj.pay.util.OrderCodeUtil;
import com.hlkj.pay.vo.openapi.merchant.req.access.OpenApiMerchantAccessQueryReq;
import com.hlkj.pay.vo.openapi.merchant.req.access.OpenApiMerchantAccessSettleQueryReq;
import com.hlkj.pay.vo.openapi.merchant.req.access.OpenApiOrderSettleSubmitReq;
import com.hlkj.pay.vo.openapi.merchant.resp.access.OpenApiMerchantAccessBalanceResp;
import com.hlkj.pay.vo.openapi.merchant.resp.access.OpenApiOrderSettleResp;

import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/12/29 16:18
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SettleOrderAppServiceImpl implements ISettleOrderAppService {

    private final ISettleOrderService settleOrderService;

    private final IMerchantAccessService merchantAccessService;

    private final OrderCodeUtil orderCodeUtil;

    private final IMerchantAccessSettleGateway merchantAccessSettleGateway;

    private final IProviderService providerService;

    private final CommonChannelCallHandler commonChannelCallHandler;

    private final OrderNotifyHandler orderNotifyHandler;

    private final IMerchantApplicationService merchantApplicationService;

    @Override
    public CommonResult<OpenApiMerchantAccessBalanceResp> accessBalance(OpenApiMerchantAccessQueryReq accessQueryReq) {
        log.info("accessBalance accessQueryReq:{}", JsonUtils.toJsonString(accessQueryReq));
        try {
            LocalMerchantRequest localMerchantRequest = (LocalMerchantRequest) LocalContext.get();
            if (StringUtils.isEmpty(accessQueryReq.getMchOrderNo()) && StringUtils.isEmpty(accessQueryReq.getRequestId()) && StringUtils.isEmpty(accessQueryReq.getMchChannelCode())) {
                return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
            }
            MerchantAccessInfoDO merchantAccessInfoDO;
            if (StringUtils.hasText(accessQueryReq.getRequestId())) {
                merchantAccessInfoDO = merchantAccessService.queryByRequestId(accessQueryReq.getRequestId());
            }else if (StringUtils.hasText(accessQueryReq.getMchChannelCode())) {
                MerchantAppChannelDO merchantAppSubChannelDO = merchantApplicationService.queryAppChannelDetail(accessQueryReq.getMchChannelCode());
                if (merchantAppSubChannelDO == null) {
                    return CommonResult.error(OpenApiResultCode.MERCHANT_APP_CHANNEL_NOT_EXIST);
                }
                merchantAccessInfoDO = new MerchantAccessInfoDO();
                merchantAccessInfoDO.setSn(merchantAppSubChannelDO.getSn());
                merchantAccessInfoDO.setChannelCode(merchantAppSubChannelDO.getChannelCode());
                merchantAccessInfoDO.setSubMchId(merchantAppSubChannelDO.getSubMchId());
                merchantAccessInfoDO.setProviderCode(merchantAppSubChannelDO.getProviderCode());
            }
            else {
                merchantAccessInfoDO = merchantAccessService.queryByMchOrderNo(localMerchantRequest.getSn(), accessQueryReq.getMchOrderNo());
            }
            if (merchantAccessInfoDO == null) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            if (!merchantAccessInfoDO.getSn().equals(localMerchantRequest.getSn())) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            boolean extensionExist = merchantAccessSettleGateway.extensionExist(merchantAccessInfoDO.getChannelCode());
            if (!extensionExist) {
                return CommonResult.error(CommonResultCode.EXTENSION_NOT_EXIST);
            }
            CommonResult<MerchantAccessBalanceDto> commonResult = merchantAccessSettleGateway.queryAccessBalance(merchantAccessInfoDO);
            if (!commonResult.isSuccess()) {
                return CommonResult.error(CommonResultCode.PARAMETER_MISSING.getCode(), commonResult.getMsg());
            }
            return CommonResult.success(BeanUtil.copyProperties(commonResult.getData(), OpenApiMerchantAccessBalanceResp.class));
        }
        catch (Exception e) {
            log.error("accessBalance error:{}", e.getMessage(), e);
            return CommonResult.error(CommonResultCode.SERVICE_ERROR);
        }
    }

    @Override
    public CommonResult<OpenApiOrderSettleResp> accessSettle(OpenApiOrderSettleSubmitReq accessAmountSubmitReq) {
        log.info("accessAmount accessAmountSubmitReq:{}", JsonUtils.toJsonString(accessAmountSubmitReq));
        try {
            LocalMerchantRequest localMerchantRequest = (LocalMerchantRequest) LocalContext.get();
            if (StringUtils.isEmpty(accessAmountSubmitReq.getMchOrderNo()) && StringUtils.isEmpty(accessAmountSubmitReq.getRequestId()) && StringUtils.isEmpty(accessAmountSubmitReq.getMchChannelCode())) {

                return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
            }
            MerchantAccessInfoDO merchantAccessInfoDO;
            if (StringUtils.hasText(accessAmountSubmitReq.getRequestId())) {
                merchantAccessInfoDO = merchantAccessService.queryByRequestId(accessAmountSubmitReq.getRequestId());
            } else if (StringUtils.hasText(accessAmountSubmitReq.getMchChannelCode())) {
                MerchantAppChannelDO merchantAppSubChannelDO = merchantApplicationService.queryAppChannelDetail(accessAmountSubmitReq.getMchChannelCode());
                if (merchantAppSubChannelDO == null) {
                    return CommonResult.error(OpenApiResultCode.MERCHANT_APP_CHANNEL_NOT_EXIST);
                }
                merchantAccessInfoDO = new MerchantAccessInfoDO();
                merchantAccessInfoDO.setSn(merchantAppSubChannelDO.getSn());
                merchantAccessInfoDO.setChannelCode(merchantAppSubChannelDO.getChannelCode());
                merchantAccessInfoDO.setSubMchId(merchantAppSubChannelDO.getSubMchId());
                merchantAccessInfoDO.setProviderCode(merchantAppSubChannelDO.getProviderCode());
            }
            else {
                merchantAccessInfoDO = merchantAccessService.queryByMchOrderNo(localMerchantRequest.getSn(), accessAmountSubmitReq.getMchOrderNo());
            }
            if (merchantAccessInfoDO == null) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            if (!merchantAccessInfoDO.getSn().equals(localMerchantRequest.getSn())) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            OrderSettleDO merchantInfoDO = settleOrderService.queryByCashOrderNo(localMerchantRequest.getSn(), accessAmountSubmitReq.getCashOrderNo());
            if (merchantInfoDO != null) {
                return CommonResult.error(OpenApiResultCode.DATA_HAS_EXIST);
            }
            boolean extensionExist = merchantAccessSettleGateway.extensionExist(merchantAccessInfoDO.getChannelCode());
            if (!extensionExist) {
                return CommonResult.error(CommonResultCode.EXTENSION_NOT_EXIST);
            }
            OrderSettleDO orderSettleDO = new OrderSettleDO();
            orderSettleDO.setSn(localMerchantRequest.getSn());
            orderSettleDO.setCashOrderNo(accessAmountSubmitReq.getCashOrderNo());
            orderSettleDO.setPayOrderNo(orderCodeUtil.orderNo(PayOrderEnums.ORDER_NO_TYPE.CASH));
            orderSettleDO.setAmount(MerchantUtils.stringToBigDecimal(accessAmountSubmitReq.getAmount()));
            orderSettleDO.setNotifyUrl(accessAmountSubmitReq.getNotifyUrl());
            orderSettleDO.setOrderStatus(MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.INIT.getCode());
            orderSettleDO.setAppId(localMerchantRequest.getAppId());
            orderSettleDO.setSignType(localMerchantRequest.getSignType());
            orderSettleDO.setRequestId(merchantAccessInfoDO.getRequestId());
            orderSettleDO.setRemark(accessAmountSubmitReq.getRemark());

            CommonResult<OrderSettleDO> commonResult = merchantAccessSettleGateway.submitAccessSettle(merchantAccessInfoDO, orderSettleDO);
            if (!commonResult.isSuccess()) {
                return CommonResult.error(commonResult.getCode(), commonResult.getMsg());
            }
            settleOrderService.addAccessAmount(orderSettleDO);
            OrderSettleDO updateAccessAmountOrderDO = commonResult.getData();
            if (updateAccessAmountOrderDO != null && !updateAccessAmountOrderDO.getOrderStatus().equals(orderSettleDO.getOrderStatus())) {
                updateAccessAmountOrderDO.setId(orderSettleDO.getId());
                orderSettleDO.setOrderStatus(updateAccessAmountOrderDO.getOrderStatus());
                orderSettleDO.setSettleDate(updateAccessAmountOrderDO.getSettleDate());
                orderSettleDO.setRemark(updateAccessAmountOrderDO.getRemark());
                orderSettleDO.setSettleFee(updateAccessAmountOrderDO.getSettleFee());
                orderSettleDO.setSuccessTime(updateAccessAmountOrderDO.getSuccessTime());
                settleOrderService.updateAccessAmountOrder(updateAccessAmountOrderDO);
            }
            return CommonResult.success(BeanUtil.copyProperties(orderSettleDO, OpenApiOrderSettleResp.class));
        }
        catch (Exception e) {
            log.error("accessSettle error:{}", e.getMessage(), e);
            return CommonResult.error(CommonResultCode.SERVICE_ERROR);
        }
    }

    @Override
    public CommonResult<OpenApiOrderSettleResp> accessSettleQuery(OpenApiMerchantAccessSettleQueryReq amountQueryReq) {
        log.info("accessAmountQuery accessQueryReq:{}", JsonUtils.toJsonString(amountQueryReq));
        try {
            LocalMerchantRequest localMerchantRequest = (LocalMerchantRequest) LocalContext.get();
            if (StringUtils.isEmpty(amountQueryReq.getPayOrderNo()) && StringUtils.isEmpty(amountQueryReq.getCashOrderNo())) {
                return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
            }
            OrderSettleDO orderSettleDO;
            if (StringUtils.hasText(amountQueryReq.getPayOrderNo())) {
                orderSettleDO = settleOrderService.queryByPayOrderNo(amountQueryReq.getPayOrderNo());
            }
            else {
                orderSettleDO = settleOrderService.queryByCashOrderNo(localMerchantRequest.getSn(), amountQueryReq.getCashOrderNo());
            }
            if (orderSettleDO == null) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            if (!orderSettleDO.getSn().equals(localMerchantRequest.getSn())) {
                return CommonResult.error(OpenApiResultCode.DATA_NOT_EXIST);
            }
            MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS merchantAccessAmountStatus = MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.from(orderSettleDO.getOrderStatus());

            switch (merchantAccessAmountStatus) {
                case SUCCESS:
                case FAIL:
                    break;
                default:
                    MerchantAccessInfoDO merchantAccessInfoDO = merchantAccessService.queryByRequestId(orderSettleDO.getRequestId());
                    OrderSettleDO updateAccessAmountOrderDO = merchantAccessSettleGateway.queryAccessSettle(merchantAccessInfoDO, orderSettleDO);
                    if (updateAccessAmountOrderDO != null && !updateAccessAmountOrderDO.getOrderStatus().equals(orderSettleDO.getOrderStatus())) {
                        updateAccessAmountOrderDO.setId(orderSettleDO.getId());
                        updatePayOrder(updateAccessAmountOrderDO, orderSettleDO);
                        orderSettleDO.setOrderStatus(updateAccessAmountOrderDO.getOrderStatus());
                        orderSettleDO.setSettleDate(updateAccessAmountOrderDO.getSettleDate());
                        orderSettleDO.setRemark(updateAccessAmountOrderDO.getRemark());
                        orderSettleDO.setSettleFee(updateAccessAmountOrderDO.getSettleFee());
                        orderSettleDO.setSuccessTime(updateAccessAmountOrderDO.getSuccessTime());

                    }

            }
            return CommonResult.success(BeanUtil.copyProperties(orderSettleDO, OpenApiOrderSettleResp.class));
        }
        catch (Exception e) {
            log.error("accessSettleQuery error:{}", e.getMessage(), e);
            return CommonResult.error(CommonResultCode.SERVICE_ERROR);
        }
    }

    @Override
    public String settleCallBack(String channelCode, String providerCode, HttpServletRequest request) {
        // 获取通道配置
        String serviceName = channelCode + "_order_settle_callback";
        ProviderChannelConfigDO providerChannelConfigDO = providerService.queryConfig(providerCode, channelCode);
        if (providerChannelConfigDO == null) {
            return commonChannelCallHandler.processAccessLogAndResponse(channelCode, providerCode, null, serviceName, null, null,
                    CommonResult.error(PayResultCode.PROVIDER_CHANNEL_NOT_EXITS));
        }
        CommonResult<OrderSettleNotifyDto> commonResult = merchantAccessSettleGateway.orderStatusParse(request, providerChannelConfigDO);
        OrderSettleNotifyDto orderSettleNotifyDto = commonResult.getData();
        OrderSettleDO updatePayOrderDO = orderSettleNotifyDto.getOrderSettleDO();
        String callBackBody = orderSettleNotifyDto.getCallBackBody();
        if (!commonResult.isSuccess()) {
            if (MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode().equals(commonResult.getCode())) {
                if (updatePayOrderDO != null && org.springframework.util.StringUtils.hasText(updatePayOrderDO.getPayOrderNo())
                        && (MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.SUCCESS.getCode().equals(updatePayOrderDO.getOrderStatus())
                                || MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode().equals(updatePayOrderDO.getOrderStatus()))) {
                    OrderSettleDO orderSettleDO = settleOrderService.queryByPayOrderNo(updatePayOrderDO.getPayOrderNo());
                    if (orderSettleDO == null) {
                        log.info("收到回调,签名错误,通过回调订单查询计算订单数据不存在 payOrderNo:{}", updatePayOrderDO.getPayOrderNo());
                        return commonResult.getMsg();
                    }
                    log.info("收到回调,签名错误,通过回调订单回查第三方支付状态 payOrderNo:{}", updatePayOrderDO.getPayOrderNo());
                    queryPayStatus(orderSettleDO);
                    return processOrderCallBack(channelCode, callBackBody, updatePayOrderDO, CommonResult.success());
                }
                else {
                    return commonResult.getMsg();
                }
            }
            return commonResult.getMsg();
        }
        return processCallBack(channelCode, callBackBody, updatePayOrderDO);
    }

    String processCallBack(String channelCode, String callBackBody, OrderSettleDO payOrderDO) {
        if (payOrderDO == null || !StringUtils.hasText(payOrderDO.getPayOrderNo()) || payOrderDO.getOrderStatus() == null) {
            return processOrderCallBack(channelCode, callBackBody, payOrderDO, CommonResult.error(CommonResultCode.PARAMETER_MISSING));
        }
        OrderSettleDO orderDb = settleOrderService.queryByPayOrderNo(payOrderDO.getPayOrderNo());
        if (orderDb == null) {
            return processOrderCallBack(channelCode, callBackBody, payOrderDO, CommonResult.error(MerchantResultCode.MERCHANT_PAY_ORDER_NOT_EXIST));
        }
        if (orderDb.getOrderStatus().equals(payOrderDO.getOrderStatus())) {
            log.info("收到回调,订单实际状态与 回调状态一致 直接返回 payOrderNo:{}", payOrderDO.getPayOrderNo());
            return processOrderCallBack(channelCode, callBackBody, orderDb, CommonResult.success());
        }
        if (payOrderDO.getSuccessTime() == null) {
            payOrderDO.setSuccessTime(System.currentTimeMillis());
        }
        // 更新数据
        updatePayOrder(payOrderDO, orderDb);
        return processOrderCallBack(channelCode, callBackBody, orderDb, CommonResult.success());
    }

    String processOrderCallBack(String channelCode, String callBackBody, OrderSettleDO orderSettleDO, CommonResult commonResult) {
        if (orderSettleDO != null) {
            MerchantAccessInfoDO merchantAccessInfoDO = merchantAccessService.queryByRequestId(orderSettleDO.getRequestId());
            LocalMerchantRequest localMerchantRequest = (LocalMerchantRequest) LocalContext.get();
            localMerchantRequest.setProviderCode(merchantAccessInfoDO.getProviderCode());
            localMerchantRequest.setChannelCode(merchantAccessInfoDO.getChannelCode());
            // localMerchantRequest.setMchChannelCode(merchantAccessInfoDO.getMchChannelCode());
            localMerchantRequest.setSn(merchantAccessInfoDO.getSn());
            // localMerchantRequest.setAppId(merchantAccessInfoDO.getAppId());
            LocalContext.set(localMerchantRequest);
        }
        return commonChannelCallHandler.processSettleLogAndResponse(channelCode, orderSettleDO.getPayOrderNo(), orderSettleDO.getPayOrderNo(), callBackBody, commonResult);
    }

    /**
     * 支付状态查询
     *
     * @param payOrderDO
     * @return
     */
    boolean queryPayStatus(OrderSettleDO payOrderDO) {
        log.info("查询支付中状态 mchOrderNo:{},state:{}", payOrderDO.getCashOrderNo(), payOrderDO.getPayOrderNo());
        MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS payOrderStatus = MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.from(payOrderDO.getOrderStatus());
        switch (payOrderStatus) {
            case DOING:
                MerchantAccessInfoDO merchantAccessInfoDO = merchantAccessService.queryByRequestId(payOrderDO.getRequestId());
                boolean extensionExist = merchantAccessSettleGateway.extensionExist(merchantAccessInfoDO.getChannelCode());
                if (extensionExist) {
                    OrderSettleDO updartePayOrderDO = merchantAccessSettleGateway.queryAccessSettle(merchantAccessInfoDO, payOrderDO);
                    if (updartePayOrderDO != null && updartePayOrderDO.getOrderStatus() != null) {
                        // 二者数据不一致 需要更新数据
                        if (!updartePayOrderDO.getOrderStatus().equals(payOrderDO.getOrderStatus())) {
                            // 更新后的熟悉进行复制
                            payOrderDO.setOrderStatus(updartePayOrderDO.getOrderStatus());
                            payOrderDO.setRemark(updartePayOrderDO.getRemark());
                            payOrderDO.setSettleFee(updartePayOrderDO.getSettleFee());
                            payOrderDO.setSuccessTime(updartePayOrderDO.getSuccessTime());
                            updatePayOrder(updartePayOrderDO, payOrderDO);
                            return false;
                        }
                    }
                    else {
                        return true;
                    }
                    break;
                }
                else {
                    log.info("当前通道不支持渠道订单功能 channelCode:{}", merchantAccessInfoDO.getChannelCode());
                    return false;
                }

        }
        return false;
    }

    void updatePayOrder(OrderSettleDO updatePayOrderDO, OrderSettleDO payOrderDO) {
        updatePayOrderDO.setId(payOrderDO.getId());
        int count = settleOrderService.updateAccessAmountOrder(updatePayOrderDO);
        // 下发状态变更通知
        if (count > 0) {
            if (MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.SUCCESS.getCode().equals(updatePayOrderDO.getOrderStatus())
                    || MerchantEnum.MERCHANT_ACCESS_AMOUNT_STATUS.FAIL.getCode().equals(updatePayOrderDO.getOrderStatus())) {
                // 统计数据
                orderNotifyHandler.orderNotify(payOrderDO.getPayOrderNo(), 1, PayOrderEnums.ORDER_STATUS_TYPE.MERCHANT_ACCESS_SETTLE_STATUS);
            }
        }
        else {
            log.info("更新订单状态,数据库返回更新的状态与原数据状态一致 直接返回 payOrderNo:{}", payOrderDO.getPayOrderNo());
        }
    }

}
